﻿// <copyright file="ItemDataView.cs" company="Oleg V. Polikarpotchkin">
// Copyright © 2008-2009 Oleg V. Polikarpotchkin. All Right Reserved
// </copyright>
// <author>Oleg V. Polikarpotchkin</author>
// <email>ov-p@yandex.ru</email>
// <date>2009-01-19</date>
// <summary>OpenWPFChart library. ItemDataView is the base class of all DataView classes.</summary>
// <revision>$Id$</revision>

using System;
using System.ComponentModel;
using System.Text;

namespace OpenWPFChart.Parts
{
	/// <summary>
	/// <see cref="ItemDataView"/> is the abstract base class of all Item DataView classes.
	/// </summary>
	/// <remarks>
	/// <see cref="ItemDataView"/> is a wrapper around an <see cref="ItemData"/> derived
	/// object and holds the reference to this object.
	/// <para><see cref="ItemDataView"/> defines the <see cref="ItemDataView.HorizontalScale"/>
	/// and <see cref="ItemDataView.VerticalScale"/> properties of a <see cref="ChartScale"/>
	/// derived type. These properties describe the dimensions of the Chart Item on the
	/// Chart Area. It also defines the <see cref="ItemDataView.Orientation"/> property
	/// which could indicate that X and Y axes should be swapped.</para>
	/// </remarks>
	public abstract class ItemDataView : DataView
	{
		#region ItemData
		ItemData itemData;
		/// <summary>
		/// Gets or sets the ItemData property.
		/// </summary>
		/// <value>ItemData value</value>
		public ItemData ItemData
		{
			get { return itemData; }
			set
			{
				if (itemData != value)
				{
					if (value != null)
					{
						if (HorizontalScale != null && !HorizontalScale.CompatibleWith(value.GetDimensionType(0)))
							throw new ArgumentException("New HorizontalScale value type doesn't match the one of data type");
						if (VerticalScale != null && !VerticalScale.CompatibleWith(value.GetDimensionType(1)))
							throw new ArgumentException("New VerticalScale value type doesn't match the one of data type");
					}

					if (itemData != null)
						itemData.PropertyChanged -= ItemDataChanged;

					itemData = value;

					if (itemData != null)
						itemData.PropertyChanged += ItemDataChanged;

					NotifyPropertyChanged("ItemData");
				}
			}
		}
		/// <summary>
		/// ItemData PropertyChanged event handler.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
		private void ItemDataChanged(object sender, PropertyChangedEventArgs e)
		{
			NotifyPropertyChanged("ItemData." + e.PropertyName);
		}
		#endregion ItemData

		#region HorizontalScale
		ChartScale horizontalScale;
		/// <summary>
		/// Gets or sets the HorizontalScale property.
		/// </summary>
		public ChartScale HorizontalScale
		{
			get { return horizontalScale; }
			set
			{
				if (horizontalScale != value)
				{
					if (value != null)
					{
						if (!value.IsConsistent)
							throw new ArgumentException("New HorizontalScale value isn't in consistent state");
						if (ItemData != null && !value.CompatibleWith(ItemData.GetDimensionType(0)))
							throw new ArgumentException("New HorizontalScale value type doesn't match the one of data type");
					}

					if (horizontalScale != null)
						horizontalScale.PropertyChanged -= HorizontalScaleChanged;

					horizontalScale = value;
					
					if (horizontalScale != null)
						horizontalScale.PropertyChanged += HorizontalScaleChanged;
					
					NotifyPropertyChanged("HorizontalScale");
				}
			}
		}
		/// <summary>
		/// HorizontalScale PropertyChanged event handler.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
		private void HorizontalScaleChanged(object sender, PropertyChangedEventArgs e)
		{
			NotifyPropertyChanged("HorizontalScale." + e.PropertyName);
		}
		#endregion HorizontalScale

		#region VerticalScale
		ChartScale verticalScale;
		/// <summary>
		/// Gets or sets the VerticalScale property.
		/// </summary>
		public ChartScale VerticalScale
		{
			get { return verticalScale; }
			set
			{
				if (verticalScale != value)
				{
					if (value != null)
					{
						if (!value.IsConsistent)
							throw new ArgumentException("New VerticalScale value isn't in consistent state");
						if (ItemData != null && !value.CompatibleWith(ItemData.GetDimensionType(1)))
							throw new ArgumentException("New VerticalScale value type doesn't match the one of data type");
					}

					if (verticalScale != null)
						verticalScale.PropertyChanged -= VerticalScaleChanged;

					verticalScale = value;
					
					if (verticalScale != null)
						verticalScale.PropertyChanged += VerticalScaleChanged;
					
					NotifyPropertyChanged("VerticalScale");
				}
			}
		}
		/// <summary>
		/// VerticalScale PropertyChanged event handler.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
		private void VerticalScaleChanged(object sender, PropertyChangedEventArgs e)
		{
			NotifyPropertyChanged("VerticalScale." + e.PropertyName);
		}
		#endregion VerticalScale

		#region Orientation
		Orientation orientation = Orientation.Horizontal;
		/// <summary>
		/// Gets or sets the Orientation property.
		/// </summary>
		/// <remarks>
		/// This porperty value is used as direction on how to draw the item.
		/// <para>It it's equal to the <c>Orientation.Horizontal</c> (the default) then the
		/// <see cref="HorizontalScale"/> is used as the horizontal axis and the <see cref="VerticalScale"/> 
		/// as the vertical one. Otherwise the axes are swapped swap.</para>
		/// </remarks>
		public Orientation Orientation
		{
			get { return orientation; }
			set
			{
				if (orientation != value)
				{
					if (value != Orientation.Horizontal && value != Orientation.Vertical)
						throw new ArgumentException("Invalid Orientation enum value", "value");
					orientation = value;
					NotifyPropertyChanged("Orientation");
				}
			}
		}
		#endregion Orientation

		#region ICloneable Overrides
		/// <summary>
		/// Creates a new object that is a copy of the current instance.
		/// </summary>
		/// <returns>
		/// A new object that is a copy of this instance.
		/// </returns>
		/// <remarks>This method is designed to provide the deep cloning facility if desired.</remarks>
		protected override object CloneInternal()
		{
			ItemDataView clone = base.CloneInternal() as ItemDataView;
			
			// Clone ItemData.
			clone.ItemData = ItemData.Clone() as ItemData;
			clone.ItemData.PropertyChanged += clone.ItemDataChanged;
			
			// Clone Scales.
			clone.HorizontalScale = HorizontalScale.Clone() as ChartScale;
			clone.HorizontalScale.PropertyChanged += clone.HorizontalScaleChanged;
			clone.VerticalScale = VerticalScale.Clone() as ChartScale;
			clone.VerticalScale.PropertyChanged += clone.VerticalScaleChanged;
			
			return clone;
		}
		#endregion ICloneable Overrides

		#region IDataErrorInfo Overrides
		/// <summary>
		/// Gets an error message indicating what is wrong with this object.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// An error message indicating what is wrong with this object. 
		/// </returns>
		protected override string ObjectErrorInfo
		{
			get 
			{
				StringBuilder sb = new StringBuilder(base.ObjectErrorInfo);
				string err = GetDataErrorInfo("HorizontalScale");
				if (!string.IsNullOrEmpty(err))
				{
					if (sb.Length > 0)
						sb.Append(Environment.NewLine);
					sb.Append(err);
				}
				err = GetDataErrorInfo("VerticalScale");
				if (!string.IsNullOrEmpty(err))
				{
					if (sb.Length > 0)
						sb.Append(Environment.NewLine);
					sb.Append(err);
				}
				return sb.ToString();
			}
		}

		/// <summary>
		/// Gets the data error info.
		/// </summary>
		/// <param name="columnName">Name of the column.</param>
		/// <returns>An error message indicating what is wrong with this column.</returns>
		protected override string GetDataErrorInfo(string columnName)
		{
			switch (columnName)
			{
				case "HorizontalScale":
					return (HorizontalScale as IDataErrorInfo).Error;
				case "VerticalScale":
					return (VerticalScale as IDataErrorInfo).Error;
			}
			return base.GetDataErrorInfo(columnName);
		}
		#endregion IDataErrorInfo Overrides
	}
}
